package function.maker;

import function.unary.UnaryFunction;

/**
 *
 * Fully Tested: No
 * Fully Documented: No
 * Created: 12-Apr-2012
 * Last Updated: 12-Apr-2012
 *
 * @author Shimu Wu
 */
public class UnaryFunctionMaker {

    /**
     * Returns the identity function
     * 
     * @param <T>
     * @return 
     */
    public static <T> UnaryFunction<T, T> identity() {
        return new UnaryFunction<T, T>() {
            public T evaluate(T e) {
                return e;
            }
        };
    }

    /**
     * Returns a composition of two functions, that is a new function
     * F(x) = f(g(x)).
     * 
     * @param <T1> the input type of g
     * @param <T2> the return type of g and the input type of f
     * @param <T3> the return type of f
     * @param f
     * @param g
     * @return 
     */
    public static <T1, T2, T3> UnaryFunction<T1, T3> compose(
            final UnaryFunction<T2, T3> f,
            final UnaryFunction<T1, T2> g) {

        // Return a composition
        return new UnaryFunction<T1, T3>() {

            public T3 evaluate(T1 e) {
                return f.evaluate(g.evaluate(e));
            }
        };
    }

    /**
     * Returns the composition of three UnaryFunctions, that is a 
     * new function F(x) = f(g(h(x))).
     * 
     * @param <T1> The input type of h
     * @param <T2> The return type of h and the input type of g
     * @param <T3> The return type of g and the input type of f
     * @param <T4> The return type of f
     * @param f
     * @param g
     * @param h
     * @return 
     */
    public static <T1, T2, T3, T4> UnaryFunction<T1, T4> compose(
            final UnaryFunction<T3, T4> f,
            final UnaryFunction<T2, T3> g,
            final UnaryFunction<T1, T2> h) {

        // Return a composition
        return new UnaryFunction<T1, T4>() {
            public T4 evaluate(T1 e) {
                return f.evaluate(g.evaluate(h.evaluate(e)));
            }
        };
    }
}
